/**
 * @preserve http://djproxy.ru
 */
djp = (
/** 
 * @param {undefined=} UNDEFINED
 * @return {function(*=)}
 */
function(UNDEFINED){
  var
  makeToggleAttach = document.addEventListener ?
  function(node, handler, eventType, useCapture){
    return function(on){
      node[on ? 'addEventListener' : 'removeEventListener'](
        eventType, handler, useCapture
      );
    }
  }:
  function(node, handler, eventType){
    return function(on){
      node[on ? 'attachEvent' : 'detachEvent']('on'+eventType, handler)
    }
  };
  /**
   * Создает новый метод more, для текущего экземплра DJProxy в цепочке. Вспомогательная функция.
   * @param {DJProxy} pastThis текущий экземпляр DJProxy в цепочке
   * @param {function(DJProxy,*,*,boolean):boolean} chkFunc возвращает true, если элемент проходит проверку в этом звене цепочки. Также может менять элемент этого звена.
   * @param {*=} a необязательный первый передаваемый параметр
   * @param {*=} b необязательный второй передаваемый параметр
   * @return {DJProxy}следующий новый экземпляр DJProxy в цепочке
   */
  function moreMaker(pastThis, chkFunc, a, b){
    var r = new DJProxy(pastThis), isFirst = true;
    /**
     * Возвращает обертку следующего, проходяшего цепочку отбора, элемента.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    r.more = function(){
      while (this.node && !chkFunc(this, a, b, isFirst)){
        this.node = pastThis.more().node;
        isFirst = true;
      }
      isFirst = false;
      return this
    }
    return r.more()
  }
  /**
   * Обертка элемента.
   * @constructor
   */
  function DJProxy(p){
    var n=document;
    if(p){
      if (p.constructor != DJProxy) {
        this.node = p.constructor != String ? p : n.getElementById(p)
      } else {
        this.node = p.node
      }
    } else {
      /* если параметр не задан, оборачиваем элемент перед текущим тегом script */
      while(n && n.nodeName!='SCRIPT')
        n=n[n['firebugIgnore']?'previousSibling':'lastChild'];
      while(n && n.nodeType!=1 || n.nodeName=='SCRIPT')n=n.previousSibling;
      this.node = n
    }
  }
  function oDJProxy(p){
    return new DJProxy(p)
  }
  DJProxy.prototype = {
    /**
     * заполняет элемент текстом, если текст не задан возвращает весь текст в элементе.
     * @this {DJProxy}
     * @param {string=} sValue
     * @return {string|DJProxy}
     */
    text: (typeof document.documentElement.innerText != 'undefined') ?
    function(sValue){
      return sValue === UNDEFINED ?
        this.node.innerText :
        (this.node.innerText = sValue, this)
    } :
    function(sValue){
      return sValue === UNDEFINED ?
        this.node.textContent :
        (this.node.textContent = sValue, this)
    },
    /**
     * Возвращает или задает атрибуту элемента значение из второго параметра.
     * @this {DJProxy}
     * @param {string} sAttrName название атрибута
     * @param {string=} sValue новое значение атрибута
     * @return {string|undefined}
     */
    attr: function(sAttrName, sValue){
      var node = this.node;
      return sValue === UNDEFINED ?
        node.nodeType == 1 && node.getAttribute(sAttrName, 2) || (
          node.getAttributeNode && (
            node = node.getAttributeNode(sAttrName)
          ) ? node.nodeValue : ''
        ) :
        (node.setAttribute(sAttrName, sValue), this)
    },
    /**
     * Возвращает обертку следующего, проходяшего цепочку отбора, элемента.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    more: function(){
      this.node = null;
      return this
    },
    /**
     * отбирает элемент с заданным названием узла.
     * @this {DJProxy}
     * @param {string} sNodeName название узла (заглавными буквами http://www.quirksmode.org/dom/w3c_core.html#t20)
     * @return {DJProxy}
     */
    hasNode: function(sNodeName){
      return moreMaker(
        this,
        function(t, sNodeName, b, isFirst){
          return isFirst && t.node.nodeName == sNodeName
        },
        sNodeName
      )
    },
    /**
     * отбирает элемент с подходящим значением атрибута.
     * @this {DJProxy}
     * @param {string} sAttrName название атрибута
     * @param {string|RegExp} expr значение атрибута, или регулярное выражение
     * @return {DJProxy}
     */
    hasAttr: function(sAttrName, expr){
      return moreMaker(
        this,
        function(t, sAttrName, expr, isFirst){
          return isFirst && (expr.test ?
            expr.test(t.attr(sAttrName)) :
            (expr == t.attr(sAttrName)))
        },
        sAttrName, expr
      )
    },
    /**
     * отбирает элемент имеющий заданный класс.
     * @this {DJProxy}
     * @param {string} sValue название класса
     * @return {DJProxy}
     */
    hasClass: function(sValue){
      return moreMaker(
        this,
        function(t, sValue, b, isFirst){
          return isFirst && (' '+t.node.className+' ').indexOf(' '+sValue+' ') != -1
        },
        sValue
      )
    },
    /**
     * отбирает сначала текущий элемент, а затем родительские.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    ancestor: function(){
      return moreMaker(
        this,
        function(t, a, b, isFirst){
          return isFirst ? t.node : t.node = t.node.parentNode
        }
      )
    },
    /**
     * отбирает предидущий элемент, из соседних.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    next: function(){
      return moreMaker(
        this,
        function(t){
          for(
            t.node = t.node.nextSibling;
            t.node && t.node.nodeType != 1;
            t.node = t.node.nextSibling
          );
          return t.node
        }
      )
    },
    /**
     * отбирает следующий элемент, из соседних.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    prev: function(){
      return moreMaker(
        this,
        function(t){
          for(
            t.node = t.node.previousSibling;
            t.node && t.node.nodeType != 1;
            t.node = t.node.previousSibling
          );
          return t.node
        }
      )
    },
    /**
     * отбирает сначала первый дочерний элемент.
     * @this {DJProxy}
     * @return {DJProxy}
     */
    child: function(){
      return moreMaker(
        this,
        function(t,a,b,isFirst){
          for(
            t.node = isFirst ? t.node.firstChild : t.node.nextSibling;
            t.node && t.node.nodeType != 1;
            t.node = t.node.nextSibling
          );
          return t.node
        }
      )
    },
    /**
     * подключает к элементу обработчик события.
     * @this {DJProxy}
     * @param {string} eventType тип события
     * @param {function(DJProxy, {X: Number, Y: Number, which: Number, prevent: function(), toggleOn: function(boolean),keyCode: Number, relatedTarget: Element, toElement: Element, fromElement: Element})} callback функция вызываемая при событии
     * @param {boolean=} useCapture если установлен, то по возможности сработает на этапе просачивания события.
     * @return {function(boolean)} функция повторно отключающая/подклчающая callback к этому событию.
     */
    attach: document.addEventListener ?
      function(eventType, callback, useCapture){
        var
        handler = function(e){
          if((e.type!='click' && e.type!='mousedown') || !e.button) {
            var r = new DJProxy(e.target),
            oEvent = {
              'X': e.pageX,
              'Y': e.pageY,
              which: e.which,
              prevent: function(){
                e.preventDefault()
              },
              toggleOn: fToggleAttach
            };
            
            if(e.keyCode) oEvent.keyCode = e.keyCode;
            if(e.relatedTarget) oEvent.relatedTarget = e.relatedTarget;
            callback(r,oEvent)
          }
        },
        fToggleAttach = makeToggleAttach(this.node, handler, eventType, useCapture);
        fToggleAttach(true);
        return fToggleAttach
      } :
      function(eventType, callback, useCapture){
        var handler = function(){
          var e = window.event,
          t = document.documentElement,
          r = new DJProxy(e.srcElement || document),
          o = {'X':'Left','Y':'Top'},
          k, scroll = 'scroll',
          oEvent = {
            prevent: function(){
              e.returnValue = false
            },
            toggleOn: fToggleAttach
          }
          for (k in o) {
            oEvent[k] = e['client'+k]+t[scroll+o[k]]
          }
          if (t = document.body) for (k in o){
            oEvent[k] += t[scroll+o[k]]
          }
          if (e.button !== UNDEFINED){
            oEvent.which = e.button & 1 ?
              1 : (
                e.button & 2 ?
                  3 : (
                    e.button & 4 ?
                      2 :
                      0
                  )
              );
          }
          if(e.keyCode) oEvent.keyCode = e.keyCode;
          if(e.toElement) oEvent.toElement = e.toElement;
          if(e.fromElement) oEvent.fromElement = e.fromElement;
          callback(r,oEvent)
        },
        fToggleAttach = makeToggleAttach(this.node, handler, eventType);
        fToggleAttach(true);
        return fToggleAttach
      }
  };
  DJProxy.prototype.constructor = DJProxy;
  return oDJProxy
})();
 /**
   * возвращает координаты элемента, относительно документа.
   * @this {DJProxy}
   * @return {DJProxy}
   */
djp(null).constructor.prototype.offset = document.documentElement.getBoundingClientRect?
  function(){
    var doc = this.node.ownerDocument,
    rootNode = doc.documentElement,
    box = this.node.getBoundingClientRect(),
    result={'Left':box.left,'Top':box.top},
    k, t;
    for (k in result){
      t='scroll'+k;
      result[k] = parseInt(
        result[k] + Math.max( rootNode[t], doc.body[t] ) - rootNode['client'+k],
        10
      )
    }
    return {
      'top': result.Top,
      'left': result.Left
    }
  }:
  function(){
  }